#pragma once

#include "../base/types.h"

OPEN_JLIB_NS


#if __GCC_ASM_FLAG_OUTPUTS__
#define X86_BIT_TEST(instruction, res_reg, idx_reg, mem_reg) \
  asm volatile (#instruction " %[idx], (%[mem])": "=@ccc"(res_reg): [idx]"r"(idx_reg), [mem]"r"(mem_reg));
#else
#define X86_BIT_TEST(instruction, res_reg, idx_reg, mem_reg) \
  asm volatile (#instruction " %[idx], (%[mem])\n\tSETC %[res]": [res]"=r"(res_reg): [idx]"r"(idx_reg), [mem]"r"(mem_reg));
#endif

#if __GCC_ASM_FLAG_OUTPUTS__
#define X86_BIT_TSTZ(instruction, res_reg, idx_reg, mem_reg) \
  asm volatile (#instruction " %[idx], (%[mem])": "=@ccae"(res_reg): [idx]"r"(idx_reg), [mem]"r"(mem_reg));
#else
#define X86_BIT_TSTZ(instruction, res_reg, idx_reg, mem_reg) \
  asm volatile (#instruction " %[idx], (%[mem])\n\tSETAE %[res]": [res]"=r"(res_reg): [idx]"r"(idx_reg), [mem]"r"(mem_reg));
#endif

template<uwl Width>
struct alignas(8) BitSet {

  inline bool test (uwl i) noexcept {
    bool r;
#if __x86_64__
    X86_BIT_TEST(BT, r, i, _bits);
#else
    auto m = 1 << (i & 7);
    auto &v = _bits[i >> 3];
    r = (v & m) != 0;
#endif
    return r;
  }

  inline bool tests (uwl i) noexcept {
    bool r;
#if __x86_64__
    X86_BIT_TEST(BTS, r, i, _bits);
#else
    auto m = 1 << (i & 7);
    auto &v = _bits[i >> 3];
    r = (v & m) != 0;
    v |= m;
#endif
    return r;
  }

  inline bool testr (uwl i) noexcept {
    bool r;
#if __x86_64__
    X86_BIT_TEST(BTR, r, i, _bits);
#else
    auto m = 1 << (i & 7);
    auto &v = _bits[i >> 3];
    r = (v & m) != 0;
    v &= ~m;
#endif
    return r;
  }

  inline bool testc (uwl i) noexcept {
    bool r;
#if __x86_64__
    X86_BIT_TEST(BTC, r, i, _bits);
#else
    auto m = 1 << (i & 7);
    auto &v = _bits[i >> 3];
    r = (v & m) != 0;
    v ^= m;
#endif
    return r;
  }

  inline bool tstz (uwl i) noexcept {
    bool r;
#if __x86_64__
    X86_BIT_TSTZ(BT, r, i, _bits);
#else
    auto m = 1 << (i & 7);
    auto &v = _bits[i >> 3];
    r = (v & m) == 0;
#endif
    return r;
  }

  inline bool tstzs (uwl i) noexcept {
    bool r;
#if __x86_64__
    X86_BIT_TSTZ(BTS, r, i, _bits);
#else
    auto m = 1 << (i & 7);
    auto &v = _bits[i >> 3];
    r = (v & m) == 0;
    v |= m;
#endif
    return r;
  }

  inline bool tstzr (uwl i) noexcept {
    bool r;
#if __x86_64__
    X86_BIT_TSTZ(BTR, r, i, _bits);
#else
    auto m = 1 << (i & 7);
    auto &v = _bits[i >> 3];
    r = (v & m) == 0;
    v &= ~m;
#endif
    return r;
  }

  inline bool tstzc (uwl i) noexcept {
    bool r;
#if __x86_64__
    X86_BIT_TSTZ(BTC, r, i, _bits);
#else
    auto m = 1 << (i & 7);
    auto &v = _bits[i >> 3];
    r = (v & m) == 0;
    v ^= m;
#endif
    return r;
  }

  inline void clear () noexcept {
    _bits = {0};
  }

private:
  u8 _bits[(Width + 7) / 8] = {0};
};


CLOSE_JLIB_NS
